package no.rmz.genetics.populationGeneration;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

/**
 * There are basically two ways of generating initial populations.
 * 
 *   o One is to find a bunch of promising individuals, and let them form the
 *     popultion to compete. 
 *     
 *   o Another is to start with a bunch of promising genes,
 *     a template individual, and generate a set of variations of the individual
 *     by systematically generating possible variations the genes.  
 *     
 * This class is a helper class for those trying to do the latter
 * 
 * This class will populate a population of E elements.  The E class must
 * have a bunch of alleles, all of these have slot-names,
 * 
 * The instantiator is primed by repeatedly invoking the  "addAllele" 
 * method for all the alleles associated with particular slots.
 * 
 * A new population is generated using the "newPopulation" method.
 */
public  abstract class PopulationInstantiator<E> {

    /**
     * A map mapping the name of a gene into a collection
     * of alleles for that gene.
     */
    private final Map<String, Collection<Allele<E>>> alleles
        = new HashMap<>();

    /**
     * Adding an allele to the collection of alleles.
     * @param geneName  The name of the gene.
     * @param alleleInstance The name of the allele.
     */
    public void addAllele(final String geneName, final Allele<E> alleleInstance) {
    	synchronized (alleles) {
    		final Collection<Allele<E>> g;

    		/*
    		 * If no alleles are present for this gene, then create a
    		 * new collection and bind it to the gene name.
    		 */
    		if (alleles.containsKey(geneName)) {
    			g = alleles.get(geneName);
    		} else {
    			final Collection<Allele<E>> group = new HashSet<>();
    			alleles.put(geneName,  group);
    			g = group;
    		}
    		g.add(alleleInstance);

        }
    }


    /**
     * Traverse the population, extract all alleles present in it.
     * The harvested population of alleles is then used to produce new
     * a new population where the alleles are spread out in a manner specified
     * by the getParamIterator method, and where the initial co-placements
     * of alleles in individuals is ignored.
     * @param population  a set of instances to harvest alleles from.
     */
    public void harvestPopulation(Collection<E> population){
     
        // XXX TBD.
    }
    
    /**
     * Generate an iterator that will provide parameter blocks for an
     * instantiator.
     * @return An iterator over parameters for new instances.
     */
    abstract protected Iterator<InstanceParamBlock<E>> getParamIterator(); 

    
    /**
     * Using the parameters provided by ther getParamIterator method,
     * produce a collection of instances that will represent the new
     * population.
     * @return A population of instances.
     */
    public Collection<E> getNewPopulation() {
        final Collection<E> result = new LinkedList<>();
        final Instantiator<E> maker = new Instantiator<>();
        for (Iterator<InstanceParamBlock<E>> i = getParamIterator();
        i.hasNext();){
            InstanceParamBlock<E> p = i.next();
            result.add(maker.instantiateFrom(p));
        }
        return result;
    }
}
